/*
 * "Copyright (c) 2008, 2009 The Regents of the University  of California.
 * All rights reserved."
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 */
#ifndef TCPLIB_H_
#define TCPLIB_H_

/* 
 * tcplib: a simple tcp implemented in a library
 * @author Stephen Dawson-Haggerty <stevedh@eecs.berkeley.edu>
 *
 *
 */

// #include <netinet/in.h>
#include <lib6lowpan/ip.h>

#define min(X,Y) (((X) > (Y)) ? (Y) : (X))
#ifndef PC
#define printf(X, args ...) dbg("stdout", X, ## args)
#define fprintf(X, Y, args ...) dbg("fprintf", Y, ## args)
#endif

typedef enum {
  TCP_CLOSED = 0,
  TCP_LISTEN,
  TCP_SYN_RCVD,
  TCP_SYN_SENT,
  TCP_ESTABLISHED,
  TCP_CLOSE_WAIT,
  TCP_LAST_ACK,
  TCP_FIN_WAIT_1,
  TCP_FIN_WAIT_2,
  TCP_CLOSING,
  TCP_TIME_WAIT,
} tcplib_sock_state_t;

enum {
  TCP_ACKPENDING  = 0x3,
  TCP_DUPACKS     = 0x3c,
  TCP_DUPACKS_OFF = 2,
  TCP_CONNECTDONE = 0x40,
  TCP_ACKSENT     = 0x80,
};

enum {
  /* how many timer tics to stay in TIME_WAIT */
  TCPLIB_TIMEWAIT_LEN = 1,
  TCPLIB_2MSL = 4,
  /* how many un-acked retransmissions before we give up the connection */
  TCPLIB_GIVEUP = 6,
};

#define GET_ACK_COUNT(X)    (((X) & TCP_DUPACKS) >> TCP_DUPACKS_OFF)
#define UNSET_ACK_COUNT(X)  ((X) &= ~TCP_DUPACKS)
#define INCR_ACK_COUNT(X)   ((X) += 1 << TCP_DUPACKS_OFF)

struct tcplib_sock {
  uint8_t flags;
  
  /* local and remote endpoints */
  struct sockaddr_in6 l_ep;
  struct sockaddr_in6 r_ep;

  /* current connection state */
  tcplib_sock_state_t state;

  void    *tx_buf;
  int tx_buf_len;

  /* max segment size, or default if
     we didn't bother to pull it out
     of the options field */
  uint16_t mss;

  uint16_t my_wind;
  /* the window the other end is
     reporting */
  uint16_t r_wind;
  uint16_t cwnd;
  uint16_t ssthresh;

  // the current next sequence number for ourgoing data.
  uint32_t seqno;
  // and the index of the last byte we've ACKed
  uint32_t ackno;

  struct {
    int8_t retx;
  } timer;

  /* retransmission counter */
  uint16_t retxcnt;

  /* this needs to be at the end so
     we can call init() on a socket
     without blowing away the linked
     list */
  struct tcplib_sock *next;
};

/* EVENTS 
 * ------------------------------------------------------------ 
 *
 * calls generated by tcplib that must be dealt with elsewhere in the
 * program.
 */


/* called when a new connection request is recieved on a socket which
 * is LISTENing.
 *
 *
 * return 0 if it wants to accept the connection and allocated a
 * buffer for it; -1 otherwise.
 */
struct tcplib_sock *tcplib_accept(struct tcplib_sock *conn,
                                  struct sockaddr_in6 *from);

/* a call-out point for tcplib to send a message */
void tcplib_send_out(struct ip6_packet *pkt, struct tcp_hdr *tcph);

/* upcall for new data; may be dispatched all the way out to a
 * handler. 
 *
 * Returns: 0 on success,
 * -1 otherwise.  The error may be safely ignored.
 */
int tcplib_process(struct ip6_hdr *ip_packet, void *payload);  

/*
 * should be called every 500ms to increment all the tcp timers
 */
int tcplib_timer_process();

/* Just fill in the fields of the socket.
 *
 * If you perform a send on a socket in this state, an ephemeral port
 * will be allocated to it.
 *
 * This must be called once on any socket that might be sent on, or
 * might have bind() called.
 */
int tcplib_init_sock(struct tcplib_sock *sock);

/* bind the socket to a local address */
int tcplib_bind(struct tcplib_sock *sock,
                struct sockaddr_in6 *addr);

/* connect the socket to a remote endpoint */
int tcplib_connect(struct tcplib_sock *sock,
                   struct sockaddr_in6 *serv_addr);


/* send data on an open socket.
 *
 * returns: 0 on success
 *   other errors
 *     - no local buffer is available,
 *     
 */
int tcplib_send(struct tcplib_sock *sock,
                 void *data, int len);

int tcplib_close(struct tcplib_sock *sock);

/* abort a connection 
 *
 * This will send a RST segment if the connection has been opened and
 * immediately return the socket to the CLOSED, uninitialized state,
 * although buffer pointers are maintained.
 *
 */
int tcplib_abort(struct tcplib_sock *sock);
#endif
